home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 406_01 / disked25 / source / diskio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-13  |  19.0 KB  |  687 lines

  1. /***
  2. *diskio.c - disk editor drive initialization/read-write module
  3. *
  4. *Copyright (c) 1991-1994, Gregg Jennings.  All wrongs reserved.
  5. *   P O Box 200, Falmouth, MA 02541-0200
  6. *
  7. *Purpose:
  8. *   MS(tm)-DOS Disk EDitor. Handles low-level DOS Sector reads/writes.
  9. *
  10. *Notice:
  11. *   This progam may be freely used and distributed.  Any distrubution
  12. *   with modifications must retain the above copyright statement and
  13. *   modifications noted.
  14. *   No pulp-publication, in whole or in part, permitted without
  15. *   permission (magazines or books).
  16. *******************************************************************************/
  17.  
  18. /*
  19.  
  20.    Version:  2.1 28-Nov-1993
  21.  
  22.    Release Notes:
  23.  
  24.    Uses Mircosoft/Borland specific CPU Register structure definitions.
  25.    MUST BE COMPILED IN LARGE MODEL (far data), the references to
  26.    SREGS will have to be modified for near data models.
  27.  
  28.    Uses DOS Interrupts 25h and 26h.  Microsoft intdos() familiy of
  29.    functions handles the popping of the flags after these Interrupts.
  30.    I have not yet verified this aspect with Borlands intdos() functions.
  31.  
  32.    Borland compilers supply functions similar to mine: absread() and
  33.    abswrite(), which correspond to sector_io().
  34.  
  35.    sector_io() can probably be easily changed to handle Interrupt 13
  36.    serivices.
  37.  
  38. */
  39.  
  40. #include <stdio.h>
  41. #include <dos.h>
  42. #include <stdlib.h>
  43. #include <malloc.h>
  44. #include <memory.h>
  45.  
  46. #include "disked.h"
  47. #include "diskio.h"
  48. #include "dpb.h"
  49. #include "alloc.h"
  50. #include "error.h"
  51.  
  52. /* DOS interrupts */
  53.  
  54. #define DOS_READ       0x25
  55. #define DOS_WRITE      0x26
  56.  
  57. /* DOS error codes */
  58.  
  59. #define FUNCTION_INVALID   1
  60. #define _DRIVE_NREADY 21
  61.  
  62. static int returnerror(int minor);
  63. static int get_block_info(int drive, struct DPB *dpb);
  64. static int sector_io(int rw, int disk, unsigned long sector, unsigned char *buffer);
  65. static int xsector_io(int rw, int disk, unsigned long sector, unsigned char *buffer);
  66.  
  67. static char ebuf[40];
  68.  
  69. /* globals for constant drive values */
  70.  
  71.     /* physical values */
  72.  
  73. unsigned long drive_size;     /* drive size in bytes */
  74. unsigned int sec_size;        /* sector size, usually 512 bytes */
  75. unsigned int max_head;        /* maximum disk head number */
  76. unsigned int max_sector;      /* maximum sector number */
  77. unsigned int max_track;       /* maximum track number */
  78. unsigned long hidden_secs;    /* number of hidden sectors on a hard drive */
  79.                               /*  Partition info, first track */
  80. unsigned int reserved_secs;   /* number of reserved sectors */
  81.                               /*  Boot sector(s) */
  82.  
  83.     /* logical (DOS) values */
  84.  
  85. unsigned long num_sectors;    /* total number of sectors */
  86. unsigned int secs_cluster;    /* sectors per cluster */
  87. unsigned int num_clusters;    /* maximum cluster no. */
  88. unsigned int cluster_size;    /* cluster size in bytes */
  89. unsigned int dir_sectors;     /* number of sectors of root */
  90. unsigned int secs_fat;        /* sectors per FAT */
  91. unsigned int num_fats;        /* number of FATs */
  92. unsigned int dir_entries;     /* number of directory entries */
  93. unsigned int data_sector;     /* first data sector */
  94. unsigned int dir_sector;      /* start of root directory */
  95. int fat_size;                 /* 12 or 16 */
  96. char format[10];              /* name of format */
  97. char volume[13];              /* volume label */
  98. char fatsize[7];              /* FAT size, DOS 4+ */
  99. unsigned int avail_clusters;
  100.  
  101. /* globals for variable drive values */
  102.  
  103. unsigned int max_drive;       /* maximum drive number */
  104. unsigned long log_sector;     /* logical sector no. */
  105. unsigned int disk;            /* drive number 0+ */
  106. unsigned int head;            /* physical head no. 0-max_head */
  107. unsigned int track;           /* physical track no. 0-max_track */
  108. unsigned int sector;          /* physical sector number 1-max_sector */
  109. unsigned char *sec_buf;       /* sector buffer */
  110.  
  111. int diskio_error;
  112.  
  113. /*
  114.  *      ERROR MESSAGES
  115.  *      Based on the PC/MS-DOS number returned in the AL
  116.  *      register by the DOS interrupts 0x25 and 0x26.
  117.  *
  118.  */
  119. static const char *module_name = "diskio";
  120. static const char *diskerr_msg[]={
  121.    "Write Protected Disk",
  122.    "Unknown Unit",
  123.    "Drive Not Ready",
  124.    "Unknown Command",
  125.    "Data Error (CRC)",
  126.    "Bad Request Structure Length",
  127.    "Seek Error",
  128.    "Unknown Media Type",
  129.    "Sector Not Found",
  130.    "Printer Out of Paper",
  131.    "Write Fault",
  132.    "Read Fault",
  133.    "General Failure",
  134.    "Reserved Error",
  135.    "Reserved Error",
  136.    "Invalid Disk Change",           /* end DOS */
  137.  
  138.    "BOOT sector invalid",
  139.    "Can not Log network drives",
  140.    "Sector out of bounds (oh oh...)",
  141.    "Not Enough Memory",
  142.  
  143. };
  144. enum DISKIO_MSGS {
  145.    DISK_BOOT_ERR = 16, DISK_REMOTE, DISK_BOUNDS, DISK_MEM_ERR,
  146.    NUM_ERRORS,
  147. };
  148.  
  149.  
  150. /*
  151.    Yuck this is confusing.  If exterror() was called to set the
  152.    error code (minor), the code may not be right (not match the
  153.    error message in diskerr_msg) on some machines.  The same
  154.    program on the same bootable floppy may give different responses
  155.    on different computers.  It is a combination of OS, Drives and
  156.    BIOS.
  157. */
  158.  
  159. static int returnerror(int minor)
  160. {
  161.    diskio_error = 1;
  162.    error.num = minor;
  163.    error.mod = module_name;
  164.    error.func = module_name;
  165.  
  166.    if (minor >= 0 && minor < NUM_ERRORS)
  167.       error.msg = diskerr_msg[minor];
  168.    /****
  169.    else if (minor == DISK_CORRUPT)
  170.       error.msg = err_msg[BOOT_ERROR];
  171.    else if (minor == DISK_MEM_ERR)
  172.       error.msg = err_msg[NO_MEM];
  173.    else if (minor == DISK_REMOTE)
  174.       error.msg = err_msg[DISK_REMOTE];
  175.    else if (minor == DISK_BOUNDS)
  176.       error.msg = err_msg[DISK_BOUNDS];
  177.    ****/
  178.    else
  179.    {
  180.       error.msg = "DOS error code: ";
  181.       sprintf(ebuf,"%02Xh",minor);
  182.       error.arg = ebuf;
  183.    }
  184.    if (minor == 0)      /* KLUDGE! */
  185.       minor = 10;
  186.    return minor;
  187. }
  188.  
  189. /*
  190.    Disk Input/Output function.
  191.  
  192.    Pass command, argument, and pointer to sector size buffer.
  193.  
  194.    Returns     DISK_OK (0) if ok
  195.                less than 0 if a known error
  196.                greater than zero for a DOS error.
  197.  
  198.    See DISKIO.H for error values.
  199.  
  200.    ver 2.0  13-Nov-1993 fixed a not free mis-match bug (INT 21, 36)
  201.             consoladated error returns
  202. */
  203.  
  204. /*
  205.    MSC 7.00 really fucked up on me when some optimizations where used
  206.    by corrupting the DPB pointer (MSC alias type problem).  This function
  207.    is "the heart" of DISKED.  It MUST be bulletproof!  The DPB actually
  208.    need not be a pointer but this function is 2-depth recursive so
  209.    stack calculations must be doubled.
  210. */
  211.  
  212. #ifdef _MSC_VER
  213. #pragma optimize("e",off)     /* don't fuck with stack frame */
  214. #endif
  215.  
  216. int diskio(int command, long arg, unsigned char *buffer)
  217. {
  218. int i;
  219. int tempd;
  220. unsigned tempu;
  221. unsigned long templ;
  222. static unsigned long saved_log_sector;
  223. unsigned long tsector;
  224. static int extended;
  225. int boot;
  226. struct DPB *dpb;
  227. struct BOOT buf;
  228. unsigned char *tbuf;
  229. union REGS regs;
  230. int error, status;
  231.  
  232.    error = status = 0;
  233.  
  234.    switch(command)      /* cases that return (if OK) are indicated */
  235.    {
  236.       case DISK_SAVE:            /* save position, returns */
  237.          saved_log_sector = log_sector;
  238.          return(DISK_OK);
  239.          break;
  240.  
  241.       case DISK_REST:            /* restore saved position, returns */
  242.          log_sector = saved_log_sector;
  243.          return(DISK_OK);
  244.          break;
  245.  
  246.       case DISK_INC:             /* move, falls through */
  247.          /*
  248.             Increment (or decrement) position, wrapping
  249.             around at the end or beginning.
  250.          */
  251.          tsector = log_sector;
  252.          if ( (log_sector+=arg) >= num_sectors)
  253.          {
  254.             if (arg<0L)
  255.                 log_sector = num_sectors + arg + tsector;
  256.             else
  257.                log_sector -= num_sectors;
  258.          }
  259.          break;
  260.  
  261.       case DISK_SET:             /* set position, falls through */
  262.          /*
  263.             Set the logical sector to the argument unless
  264.             it is not valid.
  265.          */
  266.          if (arg<0L || (unsigned long)arg>=num_sectors)
  267.          {
  268.             error = 1;
  269.             status = DISK_BOUNDS;
  270.          }
  271.          else
  272.             log_sector = arg;
  273.          break;
  274.  
  275.       case DISK_READ:            /* read sector, returns unless error */
  276.          /*
  277.           * If arg == -1 use current log_sector, else use arg.
  278.           */
  279.          tsector = (arg>=0L) ? arg : log_sector;
  280.          if (extended)
  281.             status = xsector_io(DOS_READ,disk-1,tsector,buffer);
  282.          else
  283.             status = sector_io(DOS_READ,disk-1,tsector,buffer);
  284.          if (status == -1)
  285.          {
  286.             diskio_error = 0;
  287.             if (save_sec)
  288.                 memcpy(save_sec,sec_buf,sec_size);
  289.             return(DISK_OK);
  290.          }
  291.          error = 1;
  292.          break;
  293.  
  294.       case DISK_WRITE:           /* write sector, returns unless error */
  295.          /*
  296.           * Same as DISK_READ (not too much code to duplicate it)
  297.           */
  298.          tsector = (arg>=0L) ? arg : log_sector;
  299.          if (extended)
  300.             status = xsector_io(DOS_WRITE,disk-1,tsector,buffer);
  301.          else
  302.             status = sector_io(DOS_WRITE,disk-1,tsector,buffer);
  303.          if (status == -1)
  304.          {
  305.             diskio_error = 0;
  306.             return(DISK_OK);
  307.          }
  308.          error = 1;
  309.          break;
  310.  
  311.       case DISK_INIT:            /* initialize the disk, returns unless error */
  312.          /*
  313.             Argument is 1 for A:, 2 for B: etc.
  314.             Returns:
  315.                DISK_OK,
  316.                DISK_MEM_ERR not enough memory,
  317.                DISK_BOOT_ERR bad boot sector,
  318.                DISK_NREADY drive not ready.
  319.           */
  320.  
  321.          boot = 0;
  322.          avail_clusters = 0;
  323.  
  324.          if ((dpb = (struct DPB *)alloc(1,sizeof(struct DPB))) == NULL)
  325.          {
  326.             error = 1;
  327.             status = DISK_MEM_ERR;
  328.             break;
  329.          }
  330.          error = -1;    /* if error, flag that dpb must be freed */
  331.  
  332.          /* get sector and drive size */
  333.  
  334.          if ((tempd = get_block_info((int)arg,dpb)) == 0)
  335.          {
  336.             tempu = dpb->bpb.sec_size;
  337.             templ = (dword)dpb->bpb.num_sectors;
  338.             if (templ == 0L)
  339.                templ = dpb->bpb.total_sectors;
  340.             templ *= (dword)tempu;
  341.          }
  342.          else if (tempd != FUNCTION_INVALID)
  343.          {
  344.             if (tempd == _DRIVE_NREADY)         /* catch known errors */
  345.                status = DISK_NREADY;
  346.             else
  347.                status = tempd;
  348.             break;
  349.          }
  350.  
  351.          /* use BOOT sector if get_block_info() failed or floppy drive */
  352.  
  353.          if (tempd != 0 || dpb->bpb.media_desc != 0xF8)
  354.          {
  355.             boot = 1;                           /* flag must use boot data */
  356.             regs.x.ax = 0x3600;
  357.             regs.x.dx = (int)arg;
  358.             intdos(®s,®s);
  359.             if (regs.x.ax == 0xffff)
  360.             {
  361.                status = exterror();
  362.                break;
  363.             }
  364.             tempu = regs.x.cx;
  365.             templ = (dword)tempu * (dword)regs.x.dx * (dword)regs.x.ax;
  366.             avail_clusters = regs.x.bx;
  367.          }
  368.  
  369.          /* test for extended i/o type (>32M) */
  370.  
  371.          if (templ >= 33554432L)
  372.             extended = 1;
  373.          else
  374.             extended = 0;
  375.  
  376.          /* get buffer for boot sector */
  377.  
  378.          if ((tbuf=(unsigned char *)alloc(tempu,sizeof(char)))==NULL)
  379.          {
  380.             status = DISK_MEM_ERR;
  381.             break;
  382.          }
  383.          error = -2;    /* if error, flag must free tbuf as well */
  384.  
  385.          if (extended)
  386.             status = xsector_io(DOS_READ,(int)arg-1,0,tbuf);
  387.          else
  388.             status = sector_io(DOS_READ,(int)arg-1,0,tbuf);
  389.  
  390.          if (status != -1)
  391.             break;
  392.  
  393.          /* quick and simple check for bad boot record */
  394.  
  395.          if ((tbuf[0]!=(byte)0xeb && tbuf[0]!=(byte)0xe9 && tbuf[0]!=0) \
  396.                  || (tbuf[2]!=(byte)0x90 && tbuf[2]!=0))
  397.          {
  398.             if (boot == 1)
  399.             {
  400.                status = DISK_BOOT_ERR;
  401.                break;
  402.             }
  403.          }
  404.          /* disk is valid */
  405.  
  406.          disk = (int)arg;
  407.          drive_size = templ;
  408.          sec_size = tempu;
  409.  
  410.          volume[0]='\0';
  411.          for (i=0;i<8;i++)
  412.             format[i]=tbuf[i+3];
  413.          format[i]='\0';
  414.  
  415.          if (boot == 1)
  416.             memcpy(&buf,tbuf,sizeof(struct BOOT));
  417.          else
  418.             memcpy(&buf.sec_size,&dpb->bpb,sizeof(struct BPB));
  419.  
  420.          freep(tbuf);      /* all done with these */
  421.          freep(dpb);
  422.  
  423.          secs_cluster = buf.secs_cluster;
  424.          reserved_secs = buf.reserved_secs;
  425.          num_fats = buf.num_fats;
  426.          dir_entries = buf.dir_entries;
  427.          num_sectors = (dword)buf.num_sectors;
  428.          secs_fat = buf.secs_fat;
  429.          max_sector = buf.secs_track;
  430.          max_head = buf.num_heads;
  431.          hidden_secs = buf.hidden_sectors;
  432.          if (num_sectors==0L)
  433.             num_sectors = buf.total_sectors;
  434.  
  435.          dir_sectors = dir_entries/(sec_size/32);
  436.          data_sector = (secs_fat*num_fats) + dir_sectors + reserved_secs-1;
  437.          dir_sector = (secs_fat*num_fats) + reserved_secs;
  438.          cluster_size = sec_size * secs_cluster;
  439.          drive_size = (long)sec_size * (long)num_sectors;
  440.          num_clusters =
  441.             (word)(((num_sectors-((dword)secs_fat*(dword)num_fats)-(dword)dir_sectors-1)
  442.              / (dword)secs_cluster)+1);
  443.          max_track =
  444.             (word)((((num_sectors+hidden_secs)/(dword)max_head)
  445.              / (dword)max_sector)-1);
  446.  
  447.          fat_size = (num_clusters < 4096) ? 12 : 16;
  448.          _dos_setdrive(disk,&i);
  449.  
  450.          diskio_error = 0;
  451.          return(DISK_OK);
  452.          break;
  453.  
  454.       default:
  455.          error = 1;
  456.          status = 3;
  457.          break;
  458.    }
  459.  
  460.    if (error == 0)
  461.    {
  462.       /* Set physical parameters and then read sector */
  463.  
  464.       templ = log_sector+hidden_secs;
  465.  
  466.       track  = (word)(templ/((dword)max_sector*(dword)max_head));
  467.       sector = (word)(templ%((dword)max_sector)+1);
  468.       head   = (word)(templ%((dword)max_sector*(dword)max_head));
  469.       head  /= max_sector;
  470.  
  471.       return(diskio(DISK_READ,-1L,buffer));
  472.    }
  473.  
  474.    if (error == -2)
  475.    {
  476.       freep(dpb);
  477.       freep(tbuf);
  478.    }
  479.    if (error == -1)
  480.       freep(dpb);
  481.  
  482.    return returnerror(status);
  483. }
  484. #ifdef _MSC_VER
  485. #pragma optimize("",on)
  486. #endif
  487.  
  488. /* ncluster() next or previous cluster
  489.  
  490.    Increment or decrement position by clusters.
  491.    If gone past the last cluster go to the first.
  492.    If gone before the first go to the last.
  493.  
  494.    Returns the logical sector number of the first sector
  495.    of the cluster.
  496.  */
  497.  
  498. unsigned long ncluster(register int arg)
  499. {
  500. dword temp = log_sector;
  501.  
  502.    if (arg>0)
  503.       temp+=(dword)secs_cluster;
  504.    else
  505.       temp-=(dword)secs_cluster;
  506.    temp -= clustersector(log_sector);
  507.    if (temp<data_sector || temp>=num_sectors)
  508.    {
  509.       if (arg>0)                                 /* first cluster */
  510.          return(data_sector+1);
  511.       else                                       /* last cluster */
  512.          return(clustertosector(num_clusters));
  513.    }
  514.    return(temp);
  515. }
  516.  
  517. /* Physical/Logical conversion routines.
  518.  * I figured all of this stuff out by myself with the help of a
  519.  * programmable calculator.  I have never found any of this stuff
  520.  * documented anywhere -- it may be somewhere, I just never found it.
  521.  */
  522.  
  523. /* convert logical sector number to the sector within the cluster number */
  524.  
  525. word clustersector(dword sector)
  526. {
  527.    if (sector <= (dword)data_sector)
  528.       return 0;
  529.    return (word) ((sector - (dword)(data_sector+1)) % (dword)secs_cluster);
  530. }
  531.  
  532. /* convert physical parameters to logical sector number */
  533.  
  534. dword logicalsector(word track, word sector, word head)
  535. {
  536. dword trk;
  537.  
  538.    head *= max_sector;
  539.    trk = track * max_head * max_sector;
  540.    return (trk + (dword)head + (dword)(sector-1)) - hidden_secs;
  541. }
  542.  
  543. /* convert cluster number to logical sector number */
  544.  
  545. dword clustertosector(word cluster)
  546. {
  547.    return (dword)  (((dword)(cluster-2) * (dword)secs_cluster)
  548.       + (dword)(data_sector+1));
  549. }
  550.  
  551. /* convert the logical sector number to the cluster number */
  552.  
  553. word sectortocluster(dword sector)
  554. {
  555.    if (sector <= (dword)data_sector)
  556.       return 0;
  557.    return (word)
  558.       ( ( (sector-(dword)(data_sector+1)) / (dword)secs_cluster ) + 2);
  559. }
  560.  
  561. /* get_block_info()
  562.  
  563.    Gets drive parameters from DOS Int 21 function 44 sub-function D.
  564.  
  565.    Note: The segment regs are not needed (and FP_SEG wont work)
  566.          if compiled with the SMALL memory model.
  567.  
  568.    Returns 0 for OK.
  569.  
  570. */
  571.  
  572. static int get_block_info(int drive, struct DPB *dpb)
  573. {
  574. union REGS regs;
  575. struct SREGS sregs;
  576.  
  577.    regs.x.ax=0x4409;
  578.    regs.x.bx=(unsigned)drive;
  579.    intdos(®s,®s);
  580.    if (regs.x.cflag)
  581.       return(exterror());
  582.    if (regs.x.dx & (1<<12))
  583.       return(DISK_REMOTE);
  584.    segread(&sregs);
  585.    regs.x.ax=0x440d;
  586.    regs.x.bx=(unsigned)drive;
  587.    regs.x.cx=0x0860;
  588.    regs.x.dx=FP_OFF(dpb);
  589.    sregs.ds =FP_SEG(dpb);
  590.    intdosx(®s,®s,&sregs);
  591.    if (regs.x.cflag)
  592.    {
  593.       if (regs.x.ax == 1)              /* 1 = function not supported */
  594.          return 1;                     /*  e.g. ramdrive */
  595.       else
  596.          return(exterror());
  597.    }
  598.    return(0);
  599. }
  600.  
  601. /*
  602.    Actual sector IO functions.
  603.  
  604.    Returns -1 for OK.
  605. */
  606. static sector_io(int rw, int disk, unsigned long sector, unsigned char *buffer)
  607. {
  608. union  REGS regs;
  609. struct SREGS sregs;
  610.  
  611.    regs.x.ax=disk;
  612.    regs.x.dx=(unsigned int)sector;
  613.    regs.x.cx=1U;
  614.    regs.x.bx=FP_OFF(buffer);
  615.    segread(&sregs);
  616.    sregs.ds=FP_SEG(buffer);
  617.    int86x(rw,®s,®s,&sregs);
  618.    if (regs.x.cflag)
  619.       return(regs.h.al);
  620.    return -1;
  621. }
  622.  
  623. /* Extended sector I/O */
  624.  
  625. static xsector_io(int rw, int disk, unsigned long sector, unsigned char *buffer)
  626. {
  627. union  REGS regs;
  628. struct SREGS sregs;
  629. struct DCB Dcb;
  630. struct DCB *dcb=&Dcb;
  631.  
  632.    regs.x.ax=disk;
  633.    dcb->sector=sector;
  634.    dcb->number=1U;
  635.    dcb->buffer=buffer;
  636.    regs.x.cx=0xffff;
  637.    regs.x.bx=FP_OFF(dcb);
  638.    segread(&sregs);
  639.    sregs.ds=FP_SEG(dcb);
  640.    int86x(rw,®s,®s,&sregs);
  641.    if (regs.x.cflag)
  642.       return(regs.h.al);
  643.    return -1;
  644. }
  645.  
  646. /* Get DOS Extended Error information */
  647.  
  648. #ifdef _MSC_VER
  649.  
  650. # if _MSC_VER >= 700          /* MSC v7.00 */
  651.  
  652. #pragma optimize("gle",off)
  653. #pragma warning(disable:4035)
  654.  
  655. int exterror()
  656. {
  657.    _asm  mov   ax,5900h
  658.    _asm  xor   bx,bx
  659.    _asm  int   21h
  660. }
  661. # else                        /* MSC v6.00 and QuickC v2.5 */
  662.  
  663. int exterror()
  664. {
  665. int errcode;                  /* the use of errcode is not necessary */
  666.                               /* cuz the value in AX is what is normaly */
  667.    _asm  mov   ax,5900h       /* returned, it just eliminates a compiler */
  668.    _asm  xor   bx,bx          /* warning "no return value" */
  669.    _asm  int   21h
  670.    _asm  mov   errcode,ax
  671.  
  672.    return errcode;
  673. }
  674.  
  675. # endif
  676.  
  677. #else          /* non-MSC or MSC < 6.00 */
  678.  
  679. int exterror(void)
  680. {
  681.    if (_osmajor >= 3)
  682.       return dosexterr(NULL);
  683.    return 1;
  684. }
  685.  
  686. #endif
  687.